﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using IM.Interfaces.Protocols;
using System.Windows.Forms;
using IM.Interfaces;

namespace IM.Core
{
    public class Settings : ISettings
    {
        public Dictionary<String, Object> ProtocolsSettings
        {
            get;
            set;
        }

        public Dictionary<SpecialPaths, String> Paths
        {
            get;
            set;
        }

        private IProtocolManager protocolManager;

        private string path = String.Empty;

        public Settings(IProtocolManager protocolManager)
        {
            this.protocolManager = protocolManager;
            this.ProtocolsSettings = new Dictionary<string, object>();
            this.Paths = new Dictionary<SpecialPaths, string>();

            this.Paths.Add(SpecialPaths.ProtocolsPath, "Protocols");
            this.path = "settings.xml";
        }

        public Settings(String path, IProtocolManager protocolManager)
        {
            this.protocolManager = protocolManager;
            this.ProtocolsSettings = new Dictionary<string, object>();
            this.Paths = new Dictionary<SpecialPaths, string>();

            this.path = path;

            Load(path);
        }

        public void Save()
        {
            Save(this.path);
        }

        public void Save(String path)
        {
            using (XmlTextWriter tw = new XmlTextWriter(path, Encoding.UTF8))
            {
                tw.Formatting = Formatting.Indented;

                tw.WriteStartDocument();
                tw.WriteStartElement("ConnectIM");

                    tw.WriteStartElement("Paths");

                        SavePaths(tw);

                    tw.WriteEndElement();

                    tw.WriteStartElement("Protocols");
                
                        SaveProtocols(tw);
    

                    tw.WriteEndElement();

                tw.WriteEndElement();
            }
        }

        private void SavePaths(XmlTextWriter tw)
        {
            foreach (var path in this.Paths)
            {
                tw.WriteStartElement("Path");
                tw.WriteAttributeString("type", Enum.GetName(typeof(SpecialPaths), path.Key));
                tw.WriteString(path.Value);
                tw.WriteEndElement();
            }
        }

        private void SaveProtocols(XmlTextWriter xtw)
        {
            foreach (var protocol in this.ProtocolsSettings)
            {
                string protocolName = protocol.Key.ToLower();
                XmlSerializer serializer = new XmlSerializer(this.protocolManager[protocolName].Settings.GetSettingsDataType(), String.Empty);
                XmlSerializerNamespaces xmlNamespace = new XmlSerializerNamespaces();
                xmlNamespace.Add(String.Empty, String.Empty);
                serializer.Serialize(xtw, this.ProtocolsSettings[protocolName], xmlNamespace);
            }
        }

        private void Load(String path)
        {
            using (XmlTextReader tr = new XmlTextReader(path))
            {
                while (tr.Read())
                {
                    if (tr.NodeType == XmlNodeType.Element)
                    {
                        String name = tr.LocalName;

                        switch (name)
                        {
                            case "Paths": LoadPaths(tr);
                                break;

                            case "Protocols": LoadProtocols(tr);
                                break;
                        }
                    }
                }
            }
        }

        private void LoadPaths(XmlTextReader tr)
        {
            using (XmlReader xr = tr.ReadSubtree())
            {
                xr.ReadToFollowing("Paths");
                while (xr.Read())
                {
                    if (xr.NodeType == XmlNodeType.Element)
                    {
                        LoadPath(tr);
                    }
                }
            }
        }

        private void LoadPath(XmlTextReader tr)
        {
            using (XmlReader xr = tr.ReadSubtree())
            {
                while (xr.Read())
                {
                    if (xr.NodeType == XmlNodeType.Element)
                    {
                        String type = xr.GetAttribute("type");

                        String path = xr.ReadString();

                        SpecialPaths specialPath = (SpecialPaths)Enum.Parse(typeof(SpecialPaths), type, true);

                        this.Paths.Add(specialPath, path);
                    }
                }
            }
        }

        private void LoadProtocols(XmlTextReader tr)
        {
            Directory.SetCurrentDirectory(this.Paths[SpecialPaths.ProtocolsPath]);
            AppDomain.CurrentDomain.AppendPrivatePath(Directory.GetCurrentDirectory());

            using (XmlReader xr = tr.ReadSubtree())
            {
                xr.ReadToFollowing("Protocols");
                while (xr.Read())
                {
                    if (xr.NodeType == XmlNodeType.Element)
                    {
                        LoadProtocol(tr);
                    }
                }
            }

            Directory.SetCurrentDirectory(Application.StartupPath);
        }

        private void LoadProtocol(XmlTextReader tr)
        {
            using (XmlReader xr = tr.ReadSubtree())
            {
                while (xr.Read())
                {
                    if (xr.NodeType == XmlNodeType.Element)
                    {
                        String protocolName = xr.GetAttribute("type");
                        String name = xr.GetAttribute("name");

                        if (String.IsNullOrEmpty(protocolName) || String.IsNullOrEmpty(name))
                            continue;

                        if (!this.protocolManager.Contains(name))
                        {
                            String filename = String.Format("{0}.dll", protocolName);

                            if (File.Exists(filename))
                            {
                                try
                                {
                                    this.protocolManager.CreateProtocol(name, filename);

                                    XmlSerializer serializer = new XmlSerializer(this.protocolManager[name].Settings.GetSettingsDataType(), String.Empty);
                                    Object obj = serializer.Deserialize(xr);
                                    this.ProtocolsSettings.Add(name, obj);
                                }
                                catch (Exception ex)
                                {
                                    System.Windows.Forms.MessageBox.Show(ex.Message, "Initilization error");
                                }

                            }
                        }
                    }
                }
            }

        }
    }
}
